#include "../exercise.h"

// READ: 虚函数 <https://zh.cppreference.com/w/cpp/language/virtual>

struct A {
    virtual char virtual_name() const {
        return 'A';
    }
    char direct_name() const {
        return 'A';
    }
};
struct B : public A {
    // READ: override <https://zh.cppreference.com/w/cpp/language/override>
    char virtual_name() const override {
        return 'B';
    }
    char direct_name() const {
        return 'B';
    }
};
struct C : public B {
    // READ: final <https://zh.cppreference.com/w/cpp/language/final>
    char virtual_name() const final {
        return 'C';
    }
    char direct_name() const {
        return 'C';
    }
};
struct D : public C {
    char direct_name() const {
        return 'D';
    }
    // virtual_name() 没有重写，使用C的final版本
};

int main(int argc, char **argv) {
    constexpr auto MSG = "Replace '?' with its correct name.";

    A a;
    B b;
    C c;
    D d;

    // 直接对象调用：虚函数和非虚函数都调用实际对象的版本
    ASSERT(a.virtual_name() == 'A', MSG);
    ASSERT(b.virtual_name() == 'B', MSG);
    ASSERT(c.virtual_name() == 'C', MSG);
    ASSERT(d.virtual_name() == 'C', MSG);  // D没有重写virtual_name，使用C的版本
    ASSERT(a.direct_name() == 'A', MSG);
    ASSERT(b.direct_name() == 'B', MSG);
    ASSERT(c.direct_name() == 'C', MSG);
    ASSERT(d.direct_name() == 'D', MSG);

    A &rab = b;  // A引用绑定到B对象
    B &rbc = c;  // B引用绑定到C对象
    C &rcd = d;  // C引用绑定到D对象

    // 通过引用调用虚函数：根据实际对象类型动态绑定
    ASSERT(rab.virtual_name() == 'B', MSG);  // 实际对象是B
    ASSERT(rbc.virtual_name() == 'C', MSG);  // 实际对象是C
    ASSERT(rcd.virtual_name() == 'C', MSG);  // 实际对象是D，但D没有重写，使用C的版本
    
    // 通过引用调用非虚函数：根据引用类型静态绑定
    ASSERT(rab.direct_name() == 'A', MSG);  // 引用类型是A，调用A::direct_name()
    ASSERT(rbc.direct_name() == 'B', MSG);  // 引用类型是B，调用B::direct_name()
    ASSERT(rcd.direct_name() == 'C', MSG);  // 引用类型是C，调用C::direct_name()

    A &rac = c;  // A引用绑定到C对象
    B &rbd = d;  // B引用绑定到D对象

    // 虚函数：动态绑定到实际对象
    ASSERT(rac.virtual_name() == 'C', MSG);  // 实际对象是C
    ASSERT(rbd.virtual_name() == 'C', MSG);  // 实际对象是D，但D没有重写，使用C的版本
    
    // 非虚函数：静态绑定到引用类型
    ASSERT(rac.direct_name() == 'A', MSG);  // 引用类型是A
    ASSERT(rbd.direct_name() == 'B', MSG);  // 引用类型是B

    A &rad = d;  // A引用绑定到D对象

    ASSERT(rad.virtual_name() == 'C', MSG);  // 实际对象是D，但D没有重写virtual_name，使用C的版本
    ASSERT(rad.direct_name() == 'A', MSG);   // 引用类型是A，调用A::direct_name()

    return 0;
}

// READ: 扩展阅读-纯虚、抽象 <https://zh.cppreference.com/w/cpp/language/abstract_class>
// READ: 扩展阅读-虚继承 <https://zh.cppreference.com/w/cpp/language/derived_class>


/*
虚函数

虚函数、纯虚函数

继承

父类、子类

父类的virtual函数和非virtual函数对于子类的影响

哇，感觉很难！


方法思路
虚函数 vs 非虚函数：

virtual_name() 是虚函数，支持运行时多态（虚函数，则创建的实际地址是什么类，就是使用对应的函数）
direct_name() 是非虚函数，编译时确定调用哪个版本（非虚函数，则引用、指针类型是什么，就使用对应的函数）
继承链：A → B → C → D

引用绑定：基类引用可以绑定到派生类对象

函数调用规则：

虚函数：根据实际对象类型动态绑定
非虚函数：根据引用/指针的静态类型静态绑定


为什么这么做：
使用基类new不同的子类，然后调用虚函数，会调用实际子类的重构的对应虚函数



关键概念解释
1. 虚函数（动态绑定）
A &rab = b;
rab.virtual_name();  // 返回 'B' - 根据实际对象类型b
虚函数调用在运行时根据实际对象类型确定调用哪个版本。

2. 非虚函数（静态绑定）
A &rab = b;
rab.direct_name();   // 返回 'A' - 根据引用类型A
非虚函数调用在编译时根据变量/引用的静态类型确定调用哪个版本。

3. override 和 final
override：确保函数重写了基类的虚函数
final：阻止派生类进一步重写该虚函数
4. D类的特殊情况
struct D : public C {
    char direct_name() const { return 'D'; }
    // 没有重写virtual_name，使用C的final版本
};
D类只重写了direct_name()，没有重写virtual_name()，所以virtual_name()调用会使用C类的实现。

总结表格
调用方式	虚函数结果	非虚函数结果	原因
d.virtual_name()	'C'	-	D没有重写，使用C的版本
d.direct_name()	-	'D'	D重写了非虚函数
A& rad = d; rad.virtual_name()	'C'	-	动态绑定到实际对象D，但D使用C的实现
A& rad = d; rad.direct_name()	-	'A'	静态绑定到引用类型A
这个例子很好地展示了C++中多态性的工作原理：虚函数提供运行时多态，而非虚函数提供编译时绑定。
*/